//
//  WebController.m
//  Shion
//
//  Created by Chris Karr on 12/19/07.
//  Copyright 2007-2008 Audacious Software. All rights reserved.
//

#import "WebInterface.h"
#import "Device.h"
#import "DevicesModel.h"
#import "ShionAppDelegate.h"

#define	SHION_WEB_UPDATE @"Shion Web Control"
#define	SHION_WEB_UPDATE_URL @"Shion Web Control URL"
#define SHION_WEB_UPDATE_USER @"Shion Web Control Username"
#define SHION_WEB_UPDATE_PASS @"Shion Web Control Password"

#define WEB_ACTIVE_INTERVAL 10.0
#define WEB_MAX_INTERVAL 300.0
#define WEB_INTERVAL_COUNT 5

@implementation WebInterface

- (void) upload: (NSNotification *) msg
{
	NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
	
	NSString * urlBase = [defaults stringForKey:SHION_WEB_UPDATE_URL];
	NSString * user = [defaults stringForKey:SHION_WEB_UPDATE_USER];
	NSString * pass = [defaults stringForKey:SHION_WEB_UPDATE_PASS];

	for (Device * d in [devices arrangedObjects])
	{
		NSString * address = [d addressString];
			
		NSInteger level = [d.actualStatus.lightLevel integerValue];
			
		NSString * status = @"Active";

		if (level == 0)
			status = @"Inactive";
			
		if (![status isEqual:[cache valueForKey:address]])
		{
			NSURL * cgiUrl = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%@@%@?do=upload_status&address=%@&name=%@&status=%@", 
					user, pass, urlBase, address, [d.name stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding], 
					[status stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]]];

			if (![queue containsObject:cgiUrl])
				[queue addObject:[cgiUrl retain]];
			
			[cache setValue:status forKey:address];
		}
	}
}

- (void) toggle
{
	if (updateTimer != nil)
	{
		[updateTimer invalidate];
		[updateTimer release];
	}
	
	updateTimer = nil;

	if ([[NSUserDefaults standardUserDefaults] boolForKey:SHION_WEB_UPDATE])
	{
		[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(upload:) name:DEVICES_UPDATED object:nil];
		
		lastUpdate = 0;
		
		updateTimer = [[NSTimer scheduledTimerWithTimeInterval:intervalLength target:self selector:@selector(download:) userInfo:nil 
									repeats:YES] retain];

		if (intervalLength > WEB_ACTIVE_INTERVAL)
			[updateTimer fire];
			
		[self upload:nil];
	}
	else
		[[NSNotificationCenter defaultCenter] removeObserver:self name:DEVICES_UPDATED object:nil];
}

- (void) awakeFromNib
{
	cache = [[NSMutableDictionary alloc] init];
	
	lastUpdate = 0;
	intervalLength = WEB_ACTIVE_INTERVAL;
	
	queue = [[NSMutableArray alloc] init];
	
	[[NSUserDefaults standardUserDefaults] addObserver:self forKeyPath:SHION_WEB_UPDATE options:NSKeyValueObservingOptionNew context:NULL];

	[NSTimer scheduledTimerWithTimeInterval:5.0 target:self selector:@selector(processQueue:) userInfo:nil repeats:YES];

	[self toggle];
}

- (void) processQueue:(NSTimer *) theTimer
{
	if (processing)
		return;

	if ([queue count] > 0)
	{
		processing = YES;
		
		NSURL * url = [queue objectAtIndex:0];
		
		[[NSURLConnection alloc] initWithRequest:[[NSURLRequest requestWithURL:url] retain] delegate:self startImmediately:YES];
	}
}

- (void) connection:(NSURLConnection *) connection didFailWithError:(NSError *) error
{
	processing = NO;
}

- (void) connectionDidFinishLoading:(NSURLConnection *) connection
{
	if ([queue count] > 0)
		[queue removeObjectAtIndex:0];

	processing = NO;
}

- (void) observeValueForKeyPath:(NSString *) keyPath ofObject:(id) object change:(NSDictionary *) change context:(void *) context
{
	if ([keyPath isEqual:SHION_WEB_UPDATE])
		[self toggle];
}

- (void) download:(NSTimer *) theTimer
{
	NSUserDefaults * defaults = [NSUserDefaults standardUserDefaults];
	NSString * urlBase = [defaults stringForKey:SHION_WEB_UPDATE_URL];
	NSString * user = [defaults stringForKey:SHION_WEB_UPDATE_USER];
	NSString * pass = [defaults stringForKey:SHION_WEB_UPDATE_PASS];
	NSURL * downloadUrl = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%@@%@?do=download_instructions", user, pass, urlBase]];
	
	NSXMLDocument * doc = [[NSXMLDocument alloc] initWithContentsOfURL:downloadUrl options:NSXMLDocumentTidyXML error:nil];
	NSXMLElement * root = [doc rootElement];
	
	if ([[root elementsForName:@"device"] count] == 0)
	{
		if (lastUpdate > WEB_INTERVAL_COUNT)
		{
			if (intervalLength <= WEB_ACTIVE_INTERVAL)
				intervalLength = 15.0;
			else if (intervalLength <= 15.0)
				intervalLength = 30.0;
			else if (intervalLength <= 30.0)
				intervalLength = 60.0;
			else if (intervalLength <= 60.0)
				intervalLength = 120.0;
			else 
				intervalLength = WEB_MAX_INTERVAL;
		}
		else
			lastUpdate++;
	}
	else
	{
		lastUpdate = 0;
		intervalLength = WEB_ACTIVE_INTERVAL;
		
		[self toggle];
	}
	
	for (NSXMLElement * child in [root elementsForName:@"device"])
	{
		NSMutableData * address = [NSMutableData data];
		NSString * action = nil;
		
		for (NSXMLNode * attribute in [child attributes])
		{
			if ([[attribute name] isEqual:@"address"])
			{
				NSString * addressString = [[attribute stringValue] lowercaseString];
				
				if ([addressString length] == 6)
				{
					const char * chars = [addressString cStringUsingEncoding:NSASCIIStringEncoding];
					unsigned char bytes[6];
					
					NSInteger i = 0;
					for (i = 0; i < 6; i++)
					{
						switch (chars[i])
						{
							case 'a': bytes[i] = 0xa;
								break;
							case 'b': bytes[i] = 0xb;
								break;
							case 'c': bytes[i] = 0xc;
								break;
							case 'd': bytes[i] = 0xd;
								break;
							case 'e': bytes[i] = 0xe;
								break;
							case 'f': bytes[i] = 0xf;
								break;
							default:
								bytes[i] = chars[i] % 0x10;
						}
					}
								
					unsigned char a = bytes[1] + (0x10 * bytes[0]);
					unsigned char b = bytes[3] + (0x10 * bytes[2]);
					unsigned char c = bytes[5] + (0x10 * bytes[4]);
					
					[address appendBytes:&a length:1];
					[address appendBytes:&b length:1];
					[address appendBytes:&c length:1];
				}
			}
			else if ([[attribute name] isEqual:@"action"])
				action = [attribute stringValue];
		}
		
		for (Device * d in [devices arrangedObjects])
		{
			if ([d.address isEqual:address])
			{
				if ([action isEqual:@"on"])
					d.visibleStatus.lightLevel = [NSNumber numberWithInteger:255];
				else if ([action isEqual:@"off"])
					d.visibleStatus.lightLevel = [NSNumber numberWithInteger:0];
			}
		}
	}
	
	[doc release];

	downloadUrl = [NSURL URLWithString:[NSString stringWithFormat:@"http://%@:%@@%@?do=clear_instructions", user, pass, urlBase]];
	NSURLRequest * request = [NSURLRequest requestWithURL:downloadUrl];
		
	NSURLResponse * response = nil;
	NSError * error = nil;
		
	[NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
}

@end
